<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>音频可视化</title>
    <style>
        /* 全局样式 */
        * {
            box-sizing: border-box;
        }

        html, body {
            height: 100%;
            margin: 0;
            padding: 0;
            font-family: 'Roboto', sans-serif;
        }

        /* 头部样式 */
        header {
            display: flex;
            align-items: center;
            justify-content: space-between;
            background-color: #282c34;
            color: #fff;
            padding: 1rem;
            box-shadow: 0px 3px 6px #00000029;
        }

        header h1 {
            font-size: 2rem;
            margin: 0;
            text-shadow: 2px 2px 5px #00000029;
        }

        /* 主体样式 */
        main {
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            height: 100%;
            background-color: #f5f5f5;
        }

        .container {
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            background-color: #fff;
            border-radius: 10px;
            padding: 2rem;
            box-shadow: 0px 3px 6px #00000029;
        }

        .container h2 {
            font-size: 1.5rem;
            margin: 0 0 2rem;
            text-align: center;
        }

        input[type=file] {
            display: none;
        }

        label {
            display: inline-block;
            background-color: #1abc9c;
            color: #fff;
            padding: 1rem 2rem;
            border-radius: 5px;
            cursor: pointer;
            transition: background-color 0.2s;
            text-shadow: 1px 1px #000;
            font-size: 1.2rem;
        }

        label:hover {
            background-color: #16a085;
        }

        canvas {
            display: block;
            margin: 2rem auto;
            border: solid 2px #1abc9c;
            border-radius: 10px;
            box-shadow: 1px 1px 5px #000;
        }
    </style>
</head>
<body>
<header>
    <h1>音频可视化</h1>
</header>
<main>
    <div class="container">
        <h2>选择一个音频文件</h2>
        <label for="file">选择文件</label>
        <input type="file" id="file" accept=".mp3,.wav" onchange="loadAudio()">
        <canvas id="canvas"></canvas>
    </div>
</main>

<script>
    const canvas = document.getElementById('canvas')
    const ctx = canvas.getContext('2d')

    let audioContext = null
    let audioBuffer = null
    let sourceNode = null
    let analyser = null

    function loadAudio() {
        const fileInput = document.getElementById('file')
        const file = fileInput.files[0]

        const fileReader = new FileReader()
        fileReader.onload = function() {
            const arrayBuffer = this.result
            decodeAudioData(arrayBuffer)
        }
        fileReader.readAsArrayBuffer(file)
    }

    function decodeAudioData(arrayBuffer) {
        if (audioContext === null) {
            audioContext = new AudioContext()
        }

        audioContext.decodeAudioData(arrayBuffer, function(buffer) {
            audioBuffer = buffer
            startPlayback()
        })
    }

    function startPlayback() {
        if (audioContext === null) {
            audioContext = new AudioContext()
        }
        if (sourceNode === null) {
            sourceNode = audioContext.createBufferSource()
        }
        if (analyser === null) {
            analyser = audioContext.createAnalyser()
            analyser.fftSize = 2048
        }

        sourceNode.connect(analyser)
        analyser.connect(audioContext.destination)
        sourceNode.buffer = audioBuffer

        // 解决自动播放问题
        if (audioContext.state === 'suspended') {
            audioContext.resume()
        }

        sourceNode.start()
        drawVisualization()
    }

    function drawVisualization() {
        const bufferLength = analyser.frequencyBinCount
        const dataArray = new Uint8Array(bufferLength)
        const WIDTH = canvas.width
        const HEIGHT = canvas.height
        const barWidth = WIDTH / bufferLength * 2.5
        let barHeight
        let x = 0

        function renderFrame() {
            requestAnimationFrame(renderFrame)

            analyser.getByteFrequencyData(dataArray)

            ctx.fillStyle = '#f5f5f5'
            ctx.fillRect(0, 0, WIDTH, HEIGHT)

            for (let i = 0; i < bufferLength; i++) {
                barHeight = dataArray[i]

                const r = barHeight + (25 * (i/bufferLength))
                const g = 250 * (i/bufferLength)
                const b = 50

                ctx.fillStyle = `rgb(${r},${g},${b})`
                ctx.fillRect(x, HEIGHT - barHeight / 2, barWidth, barHeight / 2)

                x += barWidth + 1
            }
            x = 0
        }
        renderFrame()
    }
</script>
</body>
</html>
